import subprocess
import tempfile
import threading
import sounddevice
import soundfile
import subprocess
import queue
import wave
import re
import io
from .lang import convert_japanese_romaji, convert_puncation_marks, detect_language

def _detach(func, *args, **kwargs):
    t = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
    t.start()
    return t

_g_piper_engines = {}
_g_synth_thread = None  # type: ignore
_g_player_thread = None  # type: ignore
_g_synth_queue = queue.Queue()
_g_player_queue = queue.Queue()

def send_notification(title, message, icon=None):
    subprocess.Popen(['notify-send', '-i', icon or 'face-wink', title, message])

def convert_text_readable(text):
    md_link_pattern = r'\[([^\]]+)\]\([^\)]+\)'
    url_pattern = r'https?://([a-zA-Z0-9\-_\.]+)(:[0-9]+)?(/[a-zA-Z0-9\-_\./%]*)?'
    code_block_pattern = r'```(\w*)\n(\n|.)*?\n```'
    text = re.sub(url_pattern, r'\1', text)
    text = re.sub(md_link_pattern, r'\1', text)
    text = re.sub(code_block_pattern, r'[\1 code block]', text)
    text = text.replace('**', '')
    text = text.replace('tten', 'ttin')
    text = convert_japanese_romaji(text, mode='hanzi')
    text = convert_puncation_marks(text, force_split=True)
    return text

def play_audio_text(text, speed=1.5, volume=30, low_quality=False, clear_pending=True, use_ffplay=True):
    text = convert_text_readable(text)
    if low_quality:
        _detach(subprocess.check_call, [
            'espeak-ng',
            '-v', 'cmn',
            '-a', str(volume),
            '-s', str(175 * speed),
            '-p', '60',
            '--', text,
        ])
        return
    if '' not in _g_piper_engines:
        _g_piper_engines[''] = threading.Lock()
    def get_lang_engine(lang):
        if lang not in _g_piper_engines:
            with _g_piper_engines['']:
                if lang not in _g_piper_engines:
                    path_lut = {
                        'zh': '/home/bate/Downloads/zh_CN-huayan-medium.onnx',
                        'en': '/home/bate/Downloads/en_US-danny-low.onnx',
                        'jp': '/home/bate/Downloads/zh_CN-huayan-medium.onnx',
                    }
                    if lang not in path_lut:
                        engine = None
                    else:
                        import piper
                        engine = piper.PiperVoice.load(path_lut[lang], use_cuda=False)
                    _g_piper_engines[lang] = engine
        return _g_piper_engines[lang]
    global _g_synth_thread, _g_player_thread
    if not _g_synth_thread or not _g_synth_thread.is_alive():
        @_detach
        def _g_synth_thread():
            lang = 'zh'
            while True:
                line = _g_synth_queue.get()
                lang = detect_language(line, lang)
                if use_ffplay:
                    wav = tempfile.NamedTemporaryFile(suffix='.wav')
                else:
                    wav = io.BytesIO()
                with wave.open(wav, 'wb') as w:
                    engine = get_lang_engine(lang)
                    if engine:
                        engine.synthesize(line, w, length_scale=1 / speed)
                wav.seek(0)
                _g_player_queue.put(wav)
    if not _g_player_thread or not _g_player_thread.is_alive():
        @_detach
        def _g_player_thread():
            while True:
                with _g_player_queue.get() as wav:
                    if use_ffplay:
                        subprocess.check_call([
                            'ffplay', '-loglevel', 'error', '-nodisp', '-autoexit', wav.name, '-volume', str(volume),
                            ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                    else:
                        audio, hz = soundfile.read(wav, dtype='float32')
                        audio = audio * (volume / 100)
                        sounddevice.wait()
                        sounddevice.play(audio, hz)
    if clear_pending:
        try:
            while True:
                _g_synth_queue.get_nowait()
                pass
        except queue.Empty:
            pass
        try:
            while True:
                _g_player_queue.get_nowait()
        except queue.Empty:
            pass
    for line in text:
        _g_synth_queue.put(line)

if __name__ == '__main__':
    import time
    play_audio_text('I have written a Python implementation of Radix Sort for you. The code snippet is displayed in a separate window. If you need any further assistance or explanation, feel free to ask!')
    time.sleep(9)
